﻿namespace HuwmanCode.Diagnostics.Logging

open System
open System.Globalization
open System.Reflection
open HuwmanCode.Diagnostics

type internal TraceHelper() =
    static let hostName =
        match Assembly.GetEntryAssembly() with 
        | null -> System.Diagnostics.Process.Current.MainModule.ModuleName
        | assembly -> assembly.GetName().Name

    static member GetTraceMessage(level:TraceLevel, message:Object, exn:Exception option)  =
        let getFileNameFromException(e:Exception) =
            if not(e.StackTrace = null) then
                let originalLineIndex = e.StackTrace.IndexOf(":line", StringComparison.Ordinal)
                if originalLineIndex > -1 then
                    let originalLine = e.StackTrace.Substring(0, originalLineIndex)
                    let sections = originalLine.Split('\\');
                    sections.[sections.Length - 1]
                else "Unavailable"
            else "Unavailable"

        let getLineNumberFromException(e:Exception) =
            let sections = if e.StackTrace = null then [||] else e.StackTrace.Split(' ')
            match sections |> Array.tryFindIndex(fun s -> s.EndsWith(":line", StringComparison.Ordinal)) with
            | None -> 0
            | Some index -> 
                let lineNumber = sections.[index + 1]
                if not(lineNumber.EndsWith("\r\n")) then Convert.ToInt32(lineNumber, CultureInfo.InvariantCulture) 
                else Convert.ToInt32(lineNumber.Substring(0, lineNumber.Length - 2), CultureInfo.InvariantCulture)

        let rec getExceptionDetail (eo:exn option) =
            match eo with
            | None -> Unchecked.defaultof<ExceptionDetail>
            | Some e -> 
                let def = "Unknown"
                let hasTargetSite = e.TargetSite <> null
                new ExceptionDetail(
                    Name = e.GetType().ToString(),
                    AssemblyName = (if hasTargetSite then e.TargetSite.Name else def),
                    TypeName = (if hasTargetSite then e.TargetSite.Name else def),
                    MethodName = (if hasTargetSite then e.TargetSite.DeclaringType.Name else def),
                    FileName = getFileNameFromException(e),
                    LineNumber = getLineNumberFromException(e),
                    Message = e.Message,
                    InnerException = 
                        if e.InnerException = null then getExceptionDetail(None) else getExceptionDetail(Some e.InnerException))
        
        new TraceMessage(Timestamp = DateTime.Now, Level = level, MachineName = Environment.MachineName,
                ExecutableName = hostName, Message = message.ToString(), ExceptionDetail = getExceptionDetail exn)